import Api from '../../http/Api';
import { tokenInterceptor } from '../Interceptors';
import { HttpUtil } from '@/utils/HttpUtil';
import { DataResponse, HttpModel, PageInfo, QueryAllCondition, QueryCondition, TimeModel } from './index';
import { DateUtil } from '@/utils/DateUtil';
import { AxiosResponse } from 'axios';
import { Overwrite } from '@/utils/types';

export abstract class TimeDataService<T extends TimeModel, K extends keyof T, O = Overwrite<T, K, string>> extends Api {
  public constructor() {
    super();
    this.create();
  }

  protected abstract create(): void;

  protected initTokenInstance = (baseURL: string): Api => {
    return this.initSingle(
      baseURL,
      {
        baseURL,
      },
      tokenInterceptor
    ) as Api;
  };

  protected abstract format(o: T): O;

  protected abstract time(o: O): T;

  selectOneById = (id: string): Promise<DataResponse<T>> =>
    this.request<HttpModel<O>>({
      method: this.method.GET,
      url: `/id/${id}`,
    }).then((resp: DataResponse<O>) => HttpUtil.setData(resp, this.time(HttpUtil.getData(resp))));

  selectOne = (record: Partial<T>): Promise<DataResponse<T>> =>
    this.request<HttpModel<O>>({
      method: this.method.POST,
      url: '/search',
      data: record ? this.format(record as T) : null,
    }).then((resp: DataResponse<O>) => HttpUtil.setData(resp, this.time(HttpUtil.getData(resp))));

  selectByIds = (list: string[]): Promise<DataResponse<T[]>> =>
    this.request<HttpModel<O[]>>({
      method: this.method.POST,
      url: '/list/id',
      data: list,
    }).then((resp: DataResponse<O[]>) =>
      HttpUtil.setData(
        resp,
        HttpUtil.getData(resp).map(record => this.time(record))
      )
    );

  select = (condition: QueryCondition<T>): Promise<DataResponse<PageInfo<T>>> =>
    this.request<HttpModel<PageInfo<O>>>({
      method: this.method.POST,
      url: '/list/search',
      data: {
        ...condition,
        query: condition.query ? this.format(condition.query as T) : null,
        columnCondition: {
          ...condition.columnCondition,
          timeConditions: condition.columnCondition?.timeConditions
            ? condition.columnCondition.timeConditions.map(timeCondition =>
                DateUtil.formatObject(timeCondition, ['begin', 'end'])
              )
            : null,
        },
      } as never,
    }).then((resp: DataResponse<PageInfo<O>>) =>
      HttpUtil.setList(
        resp,
        HttpUtil.getList(resp).map(record => this.time(record))
      )
    );

  selectLike = (condition: QueryCondition<T>): Promise<DataResponse<PageInfo<T>>> =>
    this.request<HttpModel<PageInfo<O>>>({
      method: this.method.POST,
      url: '/list/like',
      data: {
        ...condition,
        query: condition.query ? this.format(condition.query as T) : null,
        columnCondition: {
          ...condition.columnCondition,
          timeConditions: condition.columnCondition?.timeConditions
            ? condition.columnCondition.timeConditions.map(timeCondition =>
                DateUtil.formatObject(timeCondition, ['begin', 'end'])
              )
            : null,
        },
      },
    }).then((resp: DataResponse<PageInfo<O>>) =>
      HttpUtil.setList(
        resp,
        HttpUtil.getList(resp).map(record => this.time(record))
      )
    );

  selectAll = (condition: QueryAllCondition): Promise<DataResponse<PageInfo<T>>> =>
    this.request<HttpModel<PageInfo<O>>>({
      method: this.method.GET,
      url: '/list',
      data: condition,
    }).then((resp: DataResponse<PageInfo<O>>) =>
      HttpUtil.setList(
        resp,
        HttpUtil.getList(resp).map(record => this.time(record))
      )
    );

  selectCount = (record: Partial<T>): Promise<DataResponse<number>> =>
    this.request({
      method: this.method.POST,
      url: '/count',
      data: record ? this.format(record as T) : null,
    });

  insert = (record: Partial<T>): Promise<DataResponse<string>> =>
    this.request({
      method: this.method.POST,
      data: record ? this.format(record as T) : null,
    });

  insertList = (records: Partial<T>[]): Promise<DataResponse<string[]>> =>
    this.request({
      method: this.method.POST,
      url: '/list',
      data: records.map(record => (record ? this.format(record as T) : null)),
    });

  save = (record: Partial<T>): Promise<DataResponse<string>> =>
    this.request({
      method: this.method.PUT,
      data: record ? this.format(record as T) : null,
    });

  saveList = (records: Partial<T>[]): Promise<DataResponse<string[]>> =>
    this.request({
      method: this.method.PUT,
      url: '/list',
      data: records.map(record => (record ? this.format(record as T) : null)),
    });

  update = (record: Partial<T>): Promise<DataResponse<void>> =>
    this.request({
      method: this.method.PATCH,
      data: record ? this.format(record as T) : null,
    });

  updateList = (records: Partial<T>[]): Promise<DataResponse<void>> =>
    this.request({
      method: this.method.PATCH,
      url: '/list',
      data: records.map(record => (record ? this.format(record as T) : null)),
    });

  deleteById = (id: string): Promise<AxiosResponse<void>> =>
    this.request({
      method: this.method.DELETE,
      url: `/id/${id}`,
    });

  deleteByIds = (list: string[]): Promise<AxiosResponse<void>> =>
    this.request({
      method: this.method.DELETE,
      url: '/list/id',
      data: list,
    });
}
